import math
import random
import traceback
import random_word as rw
import Source.precondition as pre
from Source.matrix import Matrix
from typing import *


def isPrime(num: int) -> bool:
    if num <= 1:
        return False
    if num == 2:
        return True
    else:
        max = round(math.sqrt(num))
        for i in range(2, max + 1):
            if num % i == 0:
                return False
        return True


def printPrimeNums(**kwargs) -> List[int]:
    """
    13
    @:param kwargs : start->int , end->int
    """

    start = kwargs.get("start", 2)
    end = kwargs.get("end", 500)
    bits = len(str(end))
    pre.checkValue(start > 1)
    if start > end:
        return list()

    # count of non-prime nums
    removedCount = 0
    # Create all numbers
    allNums = [i for i in range(start, end + 1)]
    index = 0

    while index < len(allNums) - removedCount:
        val = allNums[index]
        # If current value is a prime number, check all the numbers behind
        # and move all the valid numbers to the front part of the list
        if isPrime(val):
            i, j = index + 1, index + 1
            count = len(allNums) - removedCount
            while j < count:
                num = allNums[j]
                if num % val != 0:
                    # Move to the front part
                    if i != j:
                        allNums[i] = num
                    i, j = i + 1, j + 1
                else:
                    j += 1
                    removedCount += 1
        index += 1

    length = len(allNums) - removedCount
    primeNums = allNums[:length]

    for i in range(0, length)[::5]:
        limit = 5 if i + 5 < length else length - i
        outputString = ",".join(map(
            lambda x: "{0:>{1}}".format(x, bits),
            primeNums[i:i + limit]
        )
        )
        print(outputString)


def removeDuplicateValues(**kwargs):
    """
    14
    @:param kwargs : nums->List[int]
    """
    nums = kwargs.get("nums", [35, 46, 57, 13, 24, 35, 99, 68, 13, 79, 88, 46])
    print(sorted(set(nums)))


def isIsomorphismString(**kwargs):
    """
    15
    @:param kwargs : str1->str , str2->str
    """

    str1 = kwargs.get("str1", "234")
    str2 = kwargs.get("str2", "123")

    print("str1:\"{}\" \r\nstr2:\"{}\"".format(str1, str2))

    if len(str1) != len(str2):
        print("不是同构字符串")
        return
    d = {}
    flag = True
    for c in str1:
        d[c] = d.get(c, 0) + 1
    for c in str2:
        d[c] = d.get(c, 0) - 1
        if d[c] < 0:
            flag = False
            break
        elif d[c] == 0:
            del d[c]
    print("%s同构字符串" % "不是" if not flag or len(d) != 0 else "是")


def transposeMatrix(**kwargs):
    """
    16
    @:param kwargs : size->int
    """
    size = kwargs.get("size", 4)
    matrix = Matrix(size)
    print("Initial matrix:")
    matrix.print()
    matrix.transpose()
    print("Transposed matrix:")
    matrix.print()


def gradesSort():
    """
    17
    """
    lst = [("张飞", 78, 75),
           ("李大刀", 92, 67),
           ("李墨白", 84, 88),
           ("王老虎", 50, 50),
           ("雷小米", 99, 98)]

    lst.sort(key=lambda x: x[1] + x[2], reverse=True)
    for s in lst:
        print("Name:{0}  Grade:{1}".format(s[0], s[1] + s[2]))


def createMagicMatrix(**kwargs):
    """
    18
    Solution : https://www.zhihu.com/question/30498489  @安堇然
    @:param kwargs : size->odd int
    """
    size = kwargs.get("size", 5)

    pre.checkValue(size % 2 == 1, "size must be odd number")

    matrix = Matrix(size)
    # Used to record the current position
    r, c = 0, size // 2
    # Used to record the previous position
    _r, _c = r, c
    # Used to record the count of cells that have been occupied
    tick = 0
    while tick < size ** 2:
        # Validate the position
        if r < 0 and c >= size:
            r, c = _r + 1, _c
        elif r < 0:
            r = size - 1
        elif c >= size:
            c = 0
        else:
            if matrix[r, c] == 0:
                tick += 1
                matrix[r, c] = tick
                _r, _c = r, c
                # Move towards the right upper corner
                r -= 1
                c += 1
            else:  # If the current cell has been occupied, then move down 1 cell
                r = _r + 1
                c = _c

    #  Test
    s = sum(matrix[0])
    pre.isTrue(all(sum(matrix[index]) == s for index in range(matrix.size)) and  # Test sum of each row
               all(sum(matrix[None, index]) == s for index in range(matrix.size)) and  # Test sum of each col
               sum(matrix["\\"]) == s and  # Test sum of main diagonal
               sum(matrix["/"]) == s,
               "Algorithm error!")  # Test sum of sub diagonal

    matrix.print()


def calVariance(weights: tuple = None):
    """
    48
    """
    if weights is None:
        weights = (65.5, 70.2, 100.5, 45.5, 88.8, 55.5, 73.5, 67.8)
    avg = sum(weights) / len(weights)
    variance = sum(map(lambda x: (x - avg) ** 2, weights)) / len(weights)
    print("方差为：%f" % variance)


def findKeysIntersectionOfDict():
    """
    49
    """
    d1 = {}
    d2 = {}
    for i in range(10):
        d1[random.randint(1, 10)] = d2[random.randint(1, 10)] = 1
    # 并集：set.union 交集：& 差集：^
    intersection = d1.keys() & d2.keys()
    print("字典1：{0}\n字典2：{1}\n交集为：{2}".format(d1, d2, intersection))


def findValuesIntersectionOfDict():
    """
    50
    """
    d1 = {}
    d2 = {}
    for i in range(10):
        d1[random.randint(1, 10)] = random.randint(0, 10)
        d2[random.randint(1, 10)] = random.randint(0, 10)
    intersection = set(d1.values()) & set(d2.values())
    print("字典1：{0}\n字典2：{1}\n交集为：{2}".format(d1, d2, intersection))


def createAndSortEmployees():
    """
    51
    """
    generator = rw.RandomWords()
    indices = random.sample([i for i in range(50)], 10)
    print("正在生成随机单词...")
    lst = list(zip(indices, generator.get_random_words(limit=10, minLength=3, maxLength=5)))
    lst.sort(key=lambda x: x[1])
    print("按照姓名排序：")
    for item in lst:
        print("{0:5}:{1:3}".format(item[1], item[0]))
    lst.sort(key=lambda x: x[0])
    print("按照序号排序：")
    for item in lst:
        print("{0:3}:{1:5}".format(item[0], item[1]))


def intersectionAndDifference():
    """
    52
    """
    s1 = set(random.sample([i for i in range(0, 500)], random.randint(200, 300)))
    s2 = set(random.sample([i for i in range(0, 500)], random.randint(200, 300)))

    difference = list(s1 ^ s2)
    intersection = list(s1 & s2)
    print("差集：")
    for i in range(0, len(difference), 10):
        print(" ".join("{:>5}".format(x) for x in difference[i:i + 10]))
    print("交集：")
    for i in range(0, len(intersection), 10):
        print(" ".join("{:>5}".format(x) for x in intersection[i:i + 10]))


def createRandomSetsAndValidate():
    """
    53
    """
    A = {random.randint(0, 1000) for i in range(random.randint(1, 10))}
    B = {random.randint(0, 1000) for i in range(random.randint(1, 10))}

    print("A:{} \nB:{}".format(A, B))
    valid = False
    tick = 0
    union = set.union(A, B)
    intersection = A & B
    while not valid and tick < 3:
        try:
            inputUnion = eval(input("请输入 A | B :"))
            inputIntersection = eval(input("请输入 A & B :"))
            valid = (len(union ^ inputUnion) == 0) and (len(intersection ^ inputIntersection) == 0)
        except Exception as e:
            print(e)
            print("==========")
            print(traceback.format_exc())
            valid = False

        if not valid:
            print("输入错误，请重新输入")
            tick += 1

    if tick == 3:
        print("输入错误，正确答案为：")
        print("并集：{0}".format(union))
        print("交集：{0}".format(intersection))
